using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace csammisrun.MsnLib.Utility
{
  public delegate void TransactionTimeoutHandler(Transaction trans);
  public delegate void TransactionErrorHandler(Transaction trans, int error);

  public class Transaction
  {
    private CommandProcessor _proc;
    private int _trid = -1;
    private string _command = "";
    private string _parameters = "";
    private byte[] _payload = null;
    private object _data = null;
    private MsnCommand _dependent = null;
    private Timer _timeout = null;

    public event TransactionErrorHandler TransactionError;
    public event TransactionTimeoutHandler TransactionTimeout;

    /// <summary>
    /// Initializes a new <see cref="Transaction"/>
    /// </summary>
    /// <param name="processor">The <see cref="CommandProcessor"/> that will handle the transaction</param>
    /// <param name="command">An MSN command code</param>
    /// <param name="commandformat">A format string, see <see cref="String.Format"/></param>
    /// <param name="parameters">Format parameters, see <see cref="String.Format"/></param>
    public Transaction(CommandProcessor processor, string command, string commandformat, params object[] parameters)
    {
      _proc = processor;
      _command = command;
      if (!String.IsNullOrEmpty(commandformat))
      {
        _parameters = String.Format(commandformat, parameters);
      }

      _timeout = new Timer(new TimerCallback(TimeoutOccured), null, 60000, Timeout.Infinite);
    }

    #region Properties
    public CommandProcessor CmdProc
    {
      get { return _proc; }
      set { _proc = value; }
    }

    public int ID
    {
      get { return _trid; }
      set { _trid = value; }
    }

    public string Command
    {
      get { return _command; }
      set { _command = value; }
    }

    public string Parameters
    {
      get { return _parameters; }
      set { _parameters = value; }
    }

    /// <summary>
    /// Gets or sets the binary payload for this transaction
    /// </summary>
    public byte[] Payload
    {
      get { return _payload; }
      set { _payload = value; }
    }

    /// <summary>
    /// Gets or sets a context-dependent piece of data
    /// </summary>
    public object Data
    {
      get { return _data; }
      set { _data = value; }
    }

    public MsnCommand Dependent
    {
      get
      {
        return _dependent;
      }
      set
      {
        _dependent = value;
      }
    }
    #endregion

    #region Methods
    /// <summary>
    /// Sends the command through its processor
    /// </summary>
    public void Send()
    {
      _proc.SendTransaction(this);
    }

    /// <summary>
    /// Sets the command that caused this transaction to be created
    /// </summary>
    public void SetTriggeringCommand(MsnCommand command)
    {
      _dependent = command;
    }

    /// <summary>
    /// Removes the transaction's reference to its triggering command
    /// </summary>
    public void ClearTriggeringCommand()
    {
      if (_dependent != null)
      {
        // Process the command through the proc
        _dependent = null;
      }
    }

    /// <summary>
    /// Stops the transaction's timeout countdown
    /// </summary>
    public void StopTimeout()
    {
      _timeout.Change(Timeout.Infinite, Timeout.Infinite);
    }

    /// <summary>
    /// Raise this Transaction's TransactionError event
    /// </summary>
    /// <returns><c>true</c> if an internal method handled it, <c>false</c> if the parent session needs to handle it</returns>
    public bool RaiseError(int error)
    {
      if (this.TransactionError != null)
      {
        this.TransactionError(this, error);
      }

      return (this.TransactionError != null);
    }

    /// <summary>
    /// Callback from the timeout countdown:  Raise the TransactionTimeout event
    /// </summary>
    private void TimeoutOccured(object res)
    {
      StopTimeout();

      if (this.TransactionTimeout != null)
      {
        this.TransactionTimeout(this);
      }
    }

    public override string ToString()
    {
      if(!String.IsNullOrEmpty(_parameters))
      {
        return String.Format("{0} {1} {2}\r\n", _command, _trid, _parameters);
      }
      return String.Format("{0}\r\n", _command, _trid);
    }
    #endregion
  }
}
